package com.ymx.darling.datasource.defaults;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * @author 爱Java小于
 */
public class PooledConnection implements InvocationHandler {
    private static final Class<?>[] FACES = new Class[]{Connection.class};
    private final int hashCode;
    private final PooledDataSource dataSource;
    private final Connection realConnection;
    private final Connection proxyConnection;
    private long checkoutTimestamp;
    private long createdTimestamp;
    private long lastUsedTimestamp;
    private int connectionTypeCode;
    private boolean valid;

    public PooledConnection(Connection connection, PooledDataSource dataSource) {
        this.hashCode = connection.hashCode();
        this.realConnection = connection;
        this.dataSource = dataSource;
        this.createdTimestamp = System.currentTimeMillis();
        this.lastUsedTimestamp = System.currentTimeMillis();
        this.valid = true;
        this.proxyConnection = (Connection)Proxy.newProxyInstance(Connection.class.getClassLoader(), FACES, this);
    }

    public void invalidate() {
        this.valid = false;
    }

    public boolean isValid() {
        return this.valid && this.realConnection != null && this.dataSource.pingConnection(this);
    }

    public Connection getRealConnection() {
        return this.realConnection;
    }

    public Connection getProxyConnection() {
        return this.proxyConnection;
    }

    public int getConnectionTypeCode() {
        return this.connectionTypeCode;
    }

    public void setConnectionTypeCode(int connectionTypeCode) {
        this.connectionTypeCode = connectionTypeCode;
    }

    public long getCreatedTimestamp() {
        return this.createdTimestamp;
    }

    public void setCreatedTimestamp(long createdTimestamp) {
        this.createdTimestamp = createdTimestamp;
    }

    public long getLastUsedTimestamp() {
        return this.lastUsedTimestamp;
    }

    public void setLastUsedTimestamp(long lastUsedTimestamp) {
        this.lastUsedTimestamp = lastUsedTimestamp;
    }

    public long getTimeElapsedSinceLastUse() {
        return System.currentTimeMillis() - this.lastUsedTimestamp;
    }

    public void setCheckoutTimestamp(long timestamp) {
        this.checkoutTimestamp = timestamp;
    }

    public long getCheckoutTime() {
        return System.currentTimeMillis() - this.checkoutTimestamp;
    }

    public int hashCode() {
        return this.hashCode;
    }

    public boolean equals(Object obj) {
        if (obj instanceof PooledConnection) {
            return this.realConnection.hashCode() == ((PooledConnection)obj).realConnection.hashCode();
        } else if (obj instanceof Connection) {
            return this.hashCode == obj.hashCode();
        } else {
            return false;
        }
    }

    /**
     * 执行代理逻辑
     * @param proxy Connection代理实例
     * @param method Connection代理方法
     * @param args 方法参数信息
     * @return Object
     * @throws Throwable 可能会抛出此异常
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //获取方法指定名称
        String methodName = method.getName();
        //判断执行的close方法将连接归还到连接池中
        if ("close".hashCode() == methodName.hashCode() && "close".equals(methodName)) {
            this.dataSource.pushConnection(this);
            return null;
        }else if ("isClosed".equals(methodName)) {
            return !this.valid;
        } else {
            if (!(Object.class.equals(method.getDeclaringClass()))) {
                this.checkConnection();
            }
            //调用代理目标的方法
            return method.invoke(this.realConnection, args);
        }
    }

    private void checkConnection() throws SQLException {
        if (!this.valid) {
            throw new SQLException("Error accessing PooledConnection. Connection is invalid.");
        }
    }
}

